The described implementation of the invention is described in the context of the Java language. However, the invention could be implemented in other languages such as the Microsoft Intermediate Language (MSIL).
The number of application programs written in the Java language is growing rapidly in number. One of the key reasons for this is the portability of Java code. A brief overview of Java is given below. Java is a trade mark of Sun Microsystems, Inc.
The Java language is an object-oriented programming language which is compiled to run on a Java Virtual Machine (“Java VM”). A Java VM is a computer which runs on top of the existing hardware and operating system of another computer system. Because the specifications for the Java VM have been published it is possible to write a Java VM to work with any hardware and/or operating system. Java programs are compiled into byte code, which will run on any Java VM. The Java VM essentially acts as an interpreter between the Java byte codes and the system on which the Java program is executing.
There are four main components to a Java VM, all of which are implemented in software. The four components are the registers, the operand stack, the garbage collected heap, and the method area. The method area contains the method code (i.e. the compiled Java code) and symbol tables. The compiled Java code i.e. the byte code, consists of a one byte opcode followed by any needed operands.
Compiled Java programs are typically referred to as Java class files. Many Java class files are downloaded from the Internet for execution on a user's computer system. One of the first steps performed by a Java VM is called verification. A class file verifier (part of the Java VM) ensures that the file truly is a Java class file and will execute without violating any Java security restrictions.
The class file verifier first checks to determine if the class file being loaded is of the correct class file format. The class file verifier checks to ensure that the class file being loaded is compatible with the VM loading it. The verifier also checks the information in the constant pool and other sections of the class file for consistency.
During the linking phase, the verifier ensures that all classes except for the Object class have a superclass that that all field and method references in the constant pool have valid names, classes, and type descriptors. In addition, the verifier checks the code array of the code attribute for each method to ensure that all local variables contain values of the appropriate type, that methods are classed with the appropriate arguments, and that fields are assigned correct values. The verifier also checks the operand stack for correctness.
Finally during execution, the verifier checks to ensure that a referenced type is allowed for instructions referencing a type. If an instruction modifies a field or calls a method, the verifier checks to ensure that the field or method is available and that the calling method is allowed to access the field or call the method.
Often, developers would like to measure the performance of a particular Java application in order to identify the sources of delay in the application. Some tools (like the precompiler Aspect J) could be used, however, such tools require access to the source code. Typically, however, the developer only has access to the Java class file and not to the source code. This makes it difficult to insert instrumentation code for measuring and analysing performance.
The Java runtime environment currently provides limited support for performance measurement and analysis. In addition, the class file verifier will not typically allow a class file to execute if it does not comply with all the requirements noted above. This means that instrumentation code cannot simply be added into the class file, as this would most likely cause the class file verifier to reject the class file. Similar problems occur whenever a developer desires to add, delete, or modify any code in an existing class file. For example, a developer may desire to add code for the purpose of performing benchmark tests or debugging. A developer may also desire to add, delete, or modify code for the purpose of making functional modifications to the class file.
U.S. Pat. No. 6,026,237 discloses a system and method for modifying a class file for the purpose of instrumentation, debugging, benchmarking, or making functional modifications to the class file. The method deconstructs a class file into its components and then modifies selected components by adding, deleting, or changing code within the components. The class file is then reconstructed, following all class file constraints imposed by the class file verifier.
In object-oriented programming, objects are variables comprising both routines and data that is treated as a discrete entry. A class is a category that defines a data structure and a set of methods that operate on that data. In a Java program, referred to as a class file, methods are processes performed by an object when it receives a message. When a method is called, values referred to as arguments are passed that are assigned to the corresponding parameters defined by the method and the method returns a value.
Java VM performance has been improved by a VM technology called just-in-time (JIT) compilation. In JIT, Java byte codes are converted on-the-fly into native-platform machine language. This increases the execution speed for code that is run repeatedly.
Performance analysts use many different kinds of tools including, for example, profilers of CPU time (typically sampling technology), profilers of program flow (typically event based technology), profilers of lower level system resource consumption (e.g. instruction traces, hardware performance counters). These tools do not allow the analyst to look more deeply into the behaviour of the application to understand the manner in which data is moved around and transformed by the application. Typically they report control flow, but knowledge of data flow (as with arguments and return values) is not visible. One reason for this lack of visibility is the complexity of recording this information in a generic fashion. For example, every method has a distinct signature.
Therefore analysts are often reduced to using special builds and specially instrumented code (e.g. with printfs, or System.out.println calls) to observe such detailed behaviour. While debuggers provide support for such low level visibility, they require built-in support in the virtual machine, and are typically quite heavyweight.